
#include <iostream>
#include <memory>

#include "Strategy/Animal/impl/MallardDuck.h"
#include "Strategy/Animal/impl/RubberDuck.h"
#include "Strategy/Animal/impl/DecoyDuck.h"
#include "Strategy/Animal/impl/ModelDuck.h"
#include "Strategy/Behavior/Quack/impl/FakeQuack.h"
#include "Observer/Observer/Observer.h"
#include "Observer/Subject/impl/WeatherData.h"
#include "Observer/Observer/impl/CurrentConditionsDisplay.h"
#include "Observer/Observer/impl/ForecastDisplay.h"
#include "Observer/Observer/impl/HeatIndexDisplay.h"
#include "Observer/Observer/impl/StatisticsDisplay.h"
#include "Decorator/impl/Espresso.h"
#include "Decorator/impl/Mocha.h"
#include "Decorator/impl/DarkRoast.h"
#include "Decorator/impl/Whip.h"
#include "Factory/SimpleFactory/PizzaStore.h"
#include "Factory/SimpleFactory/Pizza.h"
#include "Factory/FactoryMethod/PizzaStore/PizzaStoreFM.h"
#include "Factory/FactoryMethod/PizzaStore/Impl/ChicagoPizzaStore.h"
#include "Factory/FactoryMethod/PizzaStore/Impl/NYPizzaStore.h"
#include "Command/SimpleRemote/SimpleRemoteControl.h"
#include "Command/SimpleRemote/Pojo/Light.h"
#include "Command/SimpleRemote/Pojo/GarageDoor.h"
#include "Command/SimpleRemote/Command/Impl/LightOnCommand.h"
#include "Command/SimpleRemote/Command/Impl/GarageDoorOpenCommand.h"
#include "TemplateMethod/Barista/CaffeineBeverageWithHook.h"
#include "TemplateMethod/Barista/CoffeeWithHook.h"
#include "State/GumballState/GumballMachine.h"
#include "Compound/V1/DuckSimulator.h"
#include "test/test.h"
#include "Prototype/Machine.h"
#include "Prototype/Impl/V1Machine.h"

using namespace std;

void prototypeTest(const std::shared_ptr<Machine>& prototype){
    auto machine =  prototype->clone();
    machine->exec();
    machine->vision = "V1";
}

[[maybe_unused]] void strategyTest() {
    Strategy::MallardDuck mallardDuck;
    Strategy::RubberDuck rubberDuck;
    Strategy::DecoyDuck decoyDuck;

    Strategy::ModelDuck modelDuck;

    mallardDuck.performQuack();
    rubberDuck.performQuack();
    decoyDuck.performQuack();
    modelDuck.performQuack();
    modelDuck.setQuackBehavior(std::make_shared<Strategy::FakeQuack>());
    modelDuck.performQuack();
}

[[maybe_unused]] void observerTest() {
    Observer::WeatherData weatherData(0, 0, 0);

    std::shared_ptr<Observer::Observer> currentDisplay = std::make_shared<Observer::CurrentConditionsDisplay>();
    std::shared_ptr<Observer::Observer> forecast = std::make_shared<Observer::ForecastDisplay>();
    std::shared_ptr<Observer::Observer> heatDisplay = std::make_shared<Observer::HeatIndexDisplay>();
    std::shared_ptr<Observer::Observer> statistics = std::make_shared<Observer::StatisticsDisplay>();

    weatherData.registerObserver(currentDisplay);
    weatherData.registerObserver(forecast);
    weatherData.registerObserver(heatDisplay);
    weatherData.registerObserver(statistics);

    weatherData.setMeasurements(80, 65, 40.3f);
    weatherData.removeObserver(forecast);
    weatherData.setMeasurements(78, 90, 29.2f);
}

[[maybe_unused]] void decoratorTest() {
    std::shared_ptr<Decorator::Beverage> beverage = std::make_shared<Decorator::Espresso>();
    std::cout << beverage->getDescription() << " $" << beverage->cost() << std::endl;

    std::shared_ptr<Decorator::Beverage> beverage1 = std::make_shared<Decorator::DarkRoast>();
    beverage1 = std::make_shared<Decorator::Mocha>(beverage1);
    beverage1 = std::make_shared<Decorator::Mocha>(beverage1);
    beverage1 = std::make_shared<Decorator::Whip>(beverage1);

    std::cout << beverage1->getDescription() << " $" << beverage1->cost() << std::endl;

}

[[maybe_unused]] void testSF() {
    Factory_SF::PizzaStore store{};
    Factory_SF::Pizza pizza = store.orderPizza("cheese");
    std::cout << "We ordered a "
              << pizza.getName()
              << std::endl;
}

[[maybe_unused]] void testFM() {
    std::shared_ptr<Factory_FM::PizzaStoreFM> nyPizzaStore = std::make_shared<Factory_FM::NYPizzaStore>();
    std::shared_ptr<Factory_FM::PizzaStoreFM> chicagoPizzaStore = std::make_shared<Factory_FM::ChicagoPizzaStore>();

    auto pizza = nyPizzaStore->orderPizza("cheese");
    std::cout << pizza->getName() << std::endl;

    pizza = chicagoPizzaStore->orderPizza("cheese");
    std::cout << pizza->getName() << std::endl;
}

[[maybe_unused]] void testCommandSR() {
    CommandSR::SimpleRemoteControl remote;
    CommandSR::Light light;
    CommandSR::GarageDoor garageDoor;
    std::shared_ptr<CommandSR::Command> lightOn = std::make_shared<CommandSR::LightOnCommand>(light);
    std::shared_ptr<CommandSR::Command> garageOpen = std::make_shared<CommandSR::GarageDoorOpenCommand>(garageDoor);

    remote.setSlot(lightOn);
    remote.buttonWasPressed();
    remote.setSlot(garageOpen);
    remote.buttonWasPressed();
}

[[maybe_unused]] void testTemplateMethod() {
    std::shared_ptr<TemplateMethod_B::CaffeineBeverageWithHook> caffeine = std::make_shared<TemplateMethod_B::CoffeeWithHook>();
    caffeine->prepareRecipe();
}

[[maybe_unused]] void GumballMachineTestDrive() {
    GumballMachine gumballMachine(10);
    std::cout << gumballMachine.toString() << "\n";

    gumballMachine.insertQuarter();
    gumballMachine.turnCrank();
    std::cout << gumballMachine.toString() << "\n";

    gumballMachine.insertQuarter();
    gumballMachine.turnCrank();
    std::cout << gumballMachine.toString() << "\n";
    gumballMachine.insertQuarter();
    gumballMachine.turnCrank();
    std::cout << gumballMachine.toString() << "\n";
    gumballMachine.insertQuarter();
    gumballMachine.turnCrank();
    std::cout << gumballMachine.toString() << "\n";
    gumballMachine.insertQuarter();
    gumballMachine.turnCrank();
    std::cout << gumballMachine.toString() << "\n";
    gumballMachine.insertQuarter();
    gumballMachine.turnCrank();
    std::cout << gumballMachine.toString() << "\n";
}


int main() {
    // strategyTest();
    // observerTest();
    //decoratorTest();
    //testSF();
    //testFM();
    // testCommandSR();
    //  testTemplateMethod();

    //testTemplateMethod();


    // GumballMachineTestDrive();

    //DuckSimulator::test();

//    std::shared_ptr<Base> base = std::make_shared<Drive>();
//    base->isVirtual();
//    dynamic_pointer_cast<Drive>(base)->name();

#if 0
    {
        std::shared_ptr<Machine> machine = std::make_shared<V1Machine>();
        std::cout << machine->vision << std::endl;
        prototypeTest(machine);
        std::cout << machine->vision << std::endl;
    }
#endif

    return 0;
}
